package gogocache

import (
	"bufio"
	"fmt"
	"gitee.com/Gogo-gitee/gogo-cache/log"
	"gitee.com/Gogo-gitee/gogo-cache/persistence"
	"gitee.com/Gogo-gitee/gogo-cache/singleflight"
	"hash/crc32"
	"os"
)

// Hash 哈希函数
type Hash func(data []byte) uint32

// CacheGroup 统一管理多个cache
type CacheGroup struct {
	cacheNum  int
	getter    Getter
	mainCache []cache
	hash      Hash
	// use singleflight.Group to make sure that
	// each key is only fetched once
	loader *singleflight.Group
}

//Getter 定义获取数据源的接口
type Getter interface {
	Get(key string) ([]byte, error)
}

//GetterFunc 实现Getter接口的一个函数
type GetterFunc func(key string) ([]byte, error)

//GetterFunc 实现了getter的接口
func (f GetterFunc) Get(key string) ([]byte, error) {
	return f(key)
}

// NewGroup 新建group的实例
func NewGroup(cacheBytes int64, getter Getter, cacheNum int, fn Hash) *CacheGroup {

	caches := make([]cache, cacheNum)
	for i, _ := range caches {
		caches[i] = cache{cacheBytes: cacheBytes}
	}
	g := &CacheGroup{

		cacheNum:  cacheNum,
		getter:    getter,
		hash:      fn,
		mainCache: caches,
		loader:    &singleflight.Group{},
	}
	if g.hash == nil {
		g.hash = crc32.ChecksumIEEE
	}
	//恢复原来的键值对
	file, err := os.OpenFile("./persistence.txt", os.O_CREATE|os.O_RDONLY, 0666)
	if err != nil {
		log.Error(err)
	}
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		key := scanner.Text()
		// do anything with strLine
		if scanner.Scan() {
			value := scanner.Bytes()
			g.recover(key, ByteView{Bytes: value})
		}
	}
	return g
}

//getCacheNo 获取key对应的cache下标
func (g *CacheGroup) getCacheNo(key string) int {

	hash := int(g.hash([]byte(key))) % g.cacheNum
	return hash
}

// Get 从cache里面查找key
func (g *CacheGroup) Get(key string) (ByteView, error) {
	if key == "" {
		return ByteView{}, fmt.Errorf("key is required")
	}
	cacheNo := g.getCacheNo(key)
	log.Debugf("searching %s from %d", key, cacheNo)
	if v, ok := g.mainCache[cacheNo].get(key); ok {
		log.Debugf("[GogoCache] hit")
		return v, nil
	} else {
		if g.getter == nil {
			return ByteView{}, fmt.Errorf("[GogoCache] not hit")
		} else {
			return g.getLocally(key)
		}
	}
}

//getLocally 从数据源中获取数据
func (g *CacheGroup) getLocally(key string) (ByteView, error) {
	bytes, err := g.getter.Get(key)
	if err != nil || len(bytes) == 0 {
		return ByteView{}, err
	}
	log.Debugf("got from local %v", bytes)
	value := ByteView{Bytes: cloneBytes(bytes)}
	//回源以后，异步写入数据
	g.Set(key, value)
	return value, nil
}

//recover 恢复键值对
func (g *CacheGroup) recover(key string, value ByteView) {
	cacheNo := g.getCacheNo(key)
	g.mainCache[cacheNo].add(key, value)
}

//Set set加入键值对
func (g *CacheGroup) Set(key string, value ByteView) {
	persistence.Record(key, value.Bytes)
	cacheNo := g.getCacheNo(key)
	log.Debugf("write key: %s value: %v to cache no: %d\n", key, value, cacheNo)
	g.mainCache[cacheNo].add(key, value)
}
